﻿using System;
using System.Collections.Generic;

namespace GerbilLib
{
    /// <summary>
    /// Creates an object that holds all metadata returned from a TMDb API call.
    /// </summary>
    public class TmdbItem
    {
        /// <summary>
        /// The image sets associated with a TMDb title
        /// </summary>
        private Dictionary<string, TmdbImageSet> ImageSets { get; set; }

        #region .   Public TMDb Properties   .
        public int Popularity { get; set; }
        public bool HasTranslation { get; set; }
        public bool IsAdult { get; set; }
        public string Language { get; set; }
        public string OriginalName { get; set; }
        public string Name { get; set; }
        public string AlternativeName { get; set; }
        public string Type { get; set; }
        public int TmdbId { get; set; }
        public string ImdbId { get; set; }
        public Uri Url { get; set; }
        public string Overview { get; set; }
        public int Votes { get; set; }
        public float Rating { get; set; }
        public ContentRating ContentRating { get; set; }
        public DateTime Released { get; set; }
        public int Runtime { get; set; }
        public List<string> Genres { get; set; }
        public List<TmdbImageSet> Posters { get; set; }
        public List<TmdbImageSet> Backdrops { get; set; }
        public int Version { get; set; }
        public DateTime LastModifiedAt { get; set; }
        #endregion

        /// <summary>
        /// Initializes a new instance of the <see cref="TmdbItem"/> class.
        /// </summary>
        /// <param name="movie">The movie object containing all the metadata information.</param>
        public TmdbItem(OpenSearchDescriptionMovie movie)
        {
            Popularity = movie.popularity;
            HasTranslation = movie.translated;
            IsAdult = movie.adult;
            Language = movie.language;
            OriginalName = movie.original_name;
            Name = movie.name;
            AlternativeName = movie.alternative_name;
            Type = movie.type;
            TmdbId = movie.id;
            ImdbId = movie.imdb_id;
            Url = new Uri(movie.url);
            Overview = movie.overview;
            Votes = movie.votes;
            Rating = movie.rating;
            ContentRating = GetMpaaRating(movie.certification);
            Released = string.IsNullOrEmpty(movie.released) ? DateTime.MinValue : DateTime.Parse(movie.released);
            Runtime = string.IsNullOrEmpty(movie.runtime) ? 0 : int.Parse(movie.runtime);
            Genres = GetGenres(movie.categories);
            ImageSets = GetImageSets(movie.images);
            Posters = GetPosters(ImageSets);
            Backdrops = GetBackdrops(ImageSets);
            Version = movie.version;
            LastModifiedAt = string.IsNullOrEmpty(movie.last_modified_at) ? DateTime.MinValue : DateTime.Parse(movie.last_modified_at.Replace("UTC", "Z"));
        }

        /// <summary>
        /// Gets the MPAA content rating.
        /// </summary>
        /// <param name="certification">The text containing the content certification string.</param>
        /// <returns>MPAA Content Rating</returns>
        private ContentRating GetMpaaRating(string certification)
        {
            switch(certification)
            {
                case "PG": return ContentRating.PG;
                case "PG-13": return ContentRating.PG13;
                case "G": return ContentRating.G;
                case "NC-17": return ContentRating.NC17;
                case "R": return ContentRating.R;
                default: return ContentRating.Unknown;
            }
        }

        /// <summary>
        /// Gets the list of genres for the TMDb item.
        /// </summary>
        /// <param name="categories">The categories object containing the genres.</param>
        /// <returns>List of genres</returns>
        private List<string> GetGenres(OpenSearchDescriptionMovieCategory[] categories)
        {
            List<string> items = new List<string>();
            foreach (OpenSearchDescriptionMovieCategory category in categories)
                if (category.type == "genre")
                    items.Add(category.name);
            return items;
        }

        /// <summary>
        /// Gets the set of poster and backdrop images.
        /// </summary>
        /// <param name="images">Array of image objects from the Xml response</param>
        /// <returns>List of images</returns>
        private Dictionary<string, TmdbImageSet> GetImageSets(OpenSearchDescriptionMovieImage[] images)
        {
            Dictionary<string, TmdbImageSet> sets = new Dictionary<string,TmdbImageSet>();
            
            foreach (OpenSearchDescriptionMovieImage image in images)
            {
                TmdbImage item = new TmdbImage(image);
                TmdbImageSet set = null;
                if (sets.ContainsKey(item.Id)) set = sets[item.Id];
                else set = new TmdbImageSet(item.Id, item.Type);
                if (set != null) set.Add(item);
                if (!sets.ContainsKey(set.Id)) sets.Add(set.Id, set);
            }
            return sets;
        }

        /// <summary>
        /// Gets the list of posters from the given image set.
        /// </summary>
        /// <param name="imageSets">Image set containing images</param>
        /// <returns>List of posters</returns>
        private List<TmdbImageSet> GetPosters(Dictionary<string, TmdbImageSet> imageSets)
        {
            List<TmdbImageSet> items = new List<TmdbImageSet>();
            foreach (TmdbImageSet imageSet in imageSets.Values)
                if (imageSet.Type == TmdbImage.ImageType.Poster)
                    items.Add(imageSet);
            return items;
        }

        /// <summary>
        /// Gets the list of backdrops from the given image set.
        /// </summary>
        /// <param name="imageSets">Image set containing images</param>
        /// <returns>List of backdrops</returns>
        private List<TmdbImageSet> GetBackdrops(Dictionary<string, TmdbImageSet> imageSets)
        {
            List<TmdbImageSet> items = new List<TmdbImageSet>();
            foreach (TmdbImageSet imageSet in imageSets.Values)
                if (imageSet.Type == TmdbImage.ImageType.Backdrop)
                    items.Add(imageSet);
            return items;
        }
    }

    /// <summary>
    /// Provides a wrapper object for TMDb images such as posters and backdrops in different sizes and formats.
    /// </summary>
    public class TmdbImage
    {
        /// <summary>
        /// List of supported image types
        /// </summary>
        public enum ImageType { Poster, Backdrop, Unknown };
        /// <summary>
        /// List of supported image sizes
        /// </summary>
        public enum ImageSize { Original, Mid, w342, Cover, w154, Thumb, Poster, w1280, Unknown };
        /// <summary>
        /// Gets or sets the type of the image
        /// </summary>
        public ImageType Type { get; set; }
        /// <summary>
        /// Gets or sets the size of the image
        /// </summary>
        public ImageSize Size { get; set; }
        /// <summary>
        /// Gets or sets the Url for the image source
        /// </summary>
        public Uri Url { get; set; }
        /// <summary>
        /// Gets or sets the pixel width of the image
        /// </summary>
        public int Width { get; set; }
        /// <summary>
        /// Gets or sets the pixel height of the image
        /// </summary>
        public int Height { get; set; }
        /// <summary>
        /// Gets or sets the id of the image
        /// </summary>
        public string Id { get; set; }

        /// <summary>
        /// Creates a new TMDb image object instance.
        /// </summary>
        /// <param name="image">Xml response from TMDb API call</param>
        public TmdbImage(OpenSearchDescriptionMovieImage image)
        {
            Height = image.height;
            Width = image.width;
            Url = new Uri(image.url);
            Id = image.id;
            Type = GetImageType(image.type);
            Size = GetSize(image.size);
        }

        /// <summary>
        /// Translates the given text into an image type enumeration value.
        /// </summary>
        /// <param name="type">The image type as text</param>
        /// <returns>The image type enumeration value</returns>
        private TmdbImage.ImageType GetImageType(string type)
        {
            switch (type)
            {
                case "poster": return TmdbImage.ImageType.Poster;
                case "backdrop": return TmdbImage.ImageType.Backdrop;
                default: return TmdbImage.ImageType.Unknown;
            }
        }

        /// <summary>
        /// Translates the given text into an image size enumeration value.
        /// </summary>
        /// <param name="size">The image size as text</param>
        /// <returns>The image size enumeration value</returns>
        private TmdbImage.ImageSize GetSize(string size)
        {
            switch (size)
            {
                case "original": return ImageSize.Original;
                case "mid": return ImageSize.Mid;
                case "w342": return ImageSize.w342;
                case "cover": return ImageSize.Cover;
                case "w154": return ImageSize.w154;
                case "thumb": return ImageSize.Thumb;
                case "poster": return ImageSize.Poster;
                case "w1280": return ImageSize.w1280;
                default: return ImageSize.Unknown;
            }
        }

        /// <summary>
        /// Returns a string with the dimensions of the given TMDb image.
        /// </summary>
        /// <returns>String representation of the image</returns>
        public override string ToString()
        {
            return string.Format("{0}x{1}", Width, Height);
        }
    }

    /// <summary>
    /// Class for a set of TMDb images in different sizes
    /// </summary>
    public class TmdbImageSet
    {
        /// <summary>
        /// The list of TMDb images
        /// </summary>
        public List<TmdbImage> Images { get; set; }
        /// <summary>
        /// The original size image
        /// </summary>
        public TmdbImage Original { get; set; }
        /// <summary>
        /// The medium size image
        /// </summary>
        public TmdbImage Mid { get; set; }
        /// <summary>
        /// The cover size image
        /// </summary>
        public TmdbImage Cover { get; set; }
        /// <summary>
        /// The thumbnail size image
        /// </summary>
        public TmdbImage Thumb { get; set; }
        /// <summary>
        /// The poster size image
        /// </summary>
        public TmdbImage Poster { get; set; }
        /// <summary>
        /// The 1280 pixel wide image
        /// </summary>
        public TmdbImage W1280 { get; set; }
        /// <summary>
        /// The 154 pixel wide image
        /// </summary>
        public TmdbImage W154 { get; set; }
        /// <summary>
        /// The 342 pixel wide image
        /// </summary>
        public TmdbImage W342 { get; set; }
        /// <summary>
        /// The type of the image
        /// </summary>
        public TmdbImage.ImageType Type { get; set; }
        /// <summary>
        /// The ID of the image
        /// </summary>
        public string Id { get; set; }

        /// <summary>
        /// Creates a new TMDb Image Set object instance with the given ID and Type.
        /// </summary>
        /// <param name="id">ID of the Image Set</param>
        /// <param name="type">Image Type for the Image Set</param>
        public TmdbImageSet(string id, TmdbImage.ImageType type)
        {
            Id = id;
            Type = type;

            Images = new List<TmdbImage>();
        }

        /// <summary>
        /// Adds a new image to the image set and automatically assigns it to the
        /// corresponding size property.
        /// </summary>
        /// <param name="image">The image to be added to the set</param>
        public void Add(TmdbImage image)
        {
            if (image.Id == Id) Images.Add(image);
            switch(image.Size)
            {
                case TmdbImage.ImageSize.Cover: Cover = image; break;
                case TmdbImage.ImageSize.Mid: Mid = image; break;
                case TmdbImage.ImageSize.Original: Original = image; break;
                case TmdbImage.ImageSize.Poster: Poster = image; break;
                case TmdbImage.ImageSize.Thumb: Thumb = image; break;
                case TmdbImage.ImageSize.w1280: W1280 = image; break;
                case TmdbImage.ImageSize.w154: W154 = image; break;
                case TmdbImage.ImageSize.w342: W342 = image; break;
            }
        }
    }
}
